; Copyright 2013 padnest@gmail.com

; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;   http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.


	LIST p=18F4431
	#include <p18f4431.inc>
    #include pic18f-langext-v1.1.inc

; configs
	CONFIG OSC = HS
	CONFIG IESO = OFF
	CONFIG FCMEN = OFF
	CONFIG PWRTEN = ON
	CONFIG WDTEN = OFF
	CONFIG BOREN = OFF
	CONFIG MCLRE = ON
	CONFIG LVP = OFF
	CONFIG DEBUG = OFF
	CONFIG STVREN = ON

; registers
	UDATA
Flags		res 1       ; -<7:4> R<3> G<2> B<1> DIR<0>
Temp		res 1
SeqIndex	res 1
LED_R		res 1
LED_G		res 1
LED_B		res 1

#DEFINE Flags_Dir		Flags, 0
#DEFINE Flags_R         Flags, 3
#DEFINE Flags_G         Flags, 2
#DEFINE Flags_B         Flags, 1

; reset vector
	ORG	0x0000
	goto	main

; interrupt vector in compatibility mode
	ORG	0x0008

interrupt:
    btfsc   Flags_R
    goto    int_change_red
    btfsc   Flags_G
    goto    int_change_green
    btfsc   Flags_B
    goto    int_change_blue
    goto    int_exit

int_change_red:
    btfsc	Flags_Dir
	goto	int_change_red_inc
int_change_red_dec:
	movf    LED_R, W        ; LED_R = 0?
    bz      int_next        ; yes: load next state
    decf    LED_R, F        ; no: dec LED_R
    call    sub_write_red   ; update PWM duty cycle
    goto    int_exit        ; exit
int_change_red_inc:
    cmplf   0xFF, LED_R     ; LED_R = 0xFF?
    bz      int_next        ; yes: load next state
    incf    LED_R, F        ; no: inc LED_R
    call    sub_write_red   ; update PWM duty cycle
    goto	int_exit		; exit

int_change_green:
    btfsc	Flags_Dir
	goto	int_change_green_inc
int_change_green_dec:
	movf    LED_G, W        ; LED_G = 0?
    bz      int_next        ; yes: load next state
    decf    LED_G, F        ; no: dec LED_G
    call    sub_write_green ; update PWM duty cycle
    goto    int_exit        ; exit
int_change_green_inc:
    cmplf   0xFF, LED_G     ; LED_G = 0xFF?
    bz      int_next        ; yes: load next state
    incf    LED_G, F        ; no: inc LED_G
    call    sub_write_green ; update PWM duty cycle
    goto	int_exit		; exit

int_change_blue:
    btfsc	Flags_Dir
	goto	int_change_blue_inc
int_change_blue_dec:
	movf    LED_B, W        ; LED_B = 0?
    bz      int_next        ; yes: load next state
    decf    LED_B, F        ; no: dec LED_B
    call    sub_write_blue  ; update PWM duty cycle
    goto    int_exit        ; exit
int_change_blue_inc:
    cmplf   0xFF, LED_B     ; LED_B = 0xFF?
    bz      int_next        ; yes: load next state
    incf    LED_B, F        ; no: inc LED_B
    call    sub_write_blue  ; update PWM duty cycle
    goto	int_exit		; exit

int_next:
    incf    SeqIndex        ; inc table index (2 times in 18f family)
    incf    SeqIndex
    cmplf   0x0C, SeqIndex  ; end of table?
    bnz     int_next_get_seq
    clrf    SeqIndex        ; yes: reset index to 0
int_next_get_seq:
    movf    SeqIndex, W     ; no: get next seqeunce value
    call    get_seq
    movwf   Flags           ; and set it in Flags register

int_exit:
	bcf		PIR3, PTIF      ; re-enable PTIF interrupt
	retfie	FAST            ; exit from interrupt


get_seq:
    addwf   PCL, F          ; adjust PCL to get right sequence value
    retlw   b'00000101'     ; inc green
    retlw   b'00001000'     ; dec red
    retlw   b'00000011'     ; inc blue
    retlw   b'00000100'     ; dec green
    retlw   b'00001001'     ; inc red
    retlw   b'00000010'     ; dec blue

sub_write_red:
	movff   LED_R, Temp     ; update RED PWM duty cycle
	shftrf  Temp, 6
    movff   Temp, PDC0H
    movff   LED_R, Temp
    shftlf  Temp, 2
    movff   Temp, PDC0L
    return

sub_write_green:
	movff   LED_G, Temp     ; update GREEN PWM duty cycle
	shftrf  Temp, 6
    movff   Temp, PDC1H
    movff   LED_G, Temp
    shftlf  Temp, 2
    movff   Temp, PDC1L
    return

sub_write_blue:
	movff   LED_B, Temp     ; update BLUE PWM duty cycle
	shftrf  Temp, 6
    movff   Temp, PDC2H
    movff   LED_B, Temp
    shftlf  Temp, 2
    movff   Temp, PDC2L
    return



main:
	; init registers
    movlf   255, LED_R
    clrf    LED_G
    clrf    LED_B
    clrf    SeqIndex
    movlf   b'00000101', Flags  ; initial sequence value

	; init PWM
	movlw	b'01001000'			; event trigger postcale 1:4 + time base FOSC/64 + free-running mode
	movwf	PTCON0				; PTOPS<7:4> PTCKPS<3:2> PTMOD<1:0>
	movlw	b'00000000'			; time base off + time base count up
	movwf	PTCON1				; PTEN<7> PTDIR<6>
	movlw	b'01001111'			; PWM0&PWM1&PWM2&PWM3&PWM4&PWM5 pin enabled for out + indipendent mode for all PWM pair
	movwf	PWMCON0				; -<7> PWMEN<6:4> PMOD<3-0>
    movlw	b'00000000'			; defaults values
	movwf	PWMCON1				;

	clrf	PTPERH				; clear PTPERH
	movlw	0xFF				; time base period (8 bits)
	movwf	PTPERL				; PTPERL<7:0>

	clrf	PDC0H				; clear PDC0H
	clrf	PDC0L				; clear PDC0L
    clrf	PDC1H				; clear PDC1H
	clrf	PDC1L				; clear PDC1L
    clrf	PDC2H				; clear PDC2H
	clrf	PDC2L				; clear PDC2L


	; init interrupts
	bcf		RCON, IPEN			; disable high priority interrupts mode
	movlw	b'11000000'			; GIE + PEIE enabled
	movwf	INTCON				; GIE/GIEH<7> PEIE/GIEL<6> TMR0IE<5> INT0IE<4> RBIE<3> TMR0IF<2> INT0IF<1> RBIF<0>
	movlw	b'00010000'			; PWM Time Base Interrupt enabled (PTIE)
	movwf	PIE3				; -<7:5> PTIE<4> IC3DRIE<3> IC2QEIE<2> IC1IE<1> TMR5IE<0>
	clrf	PIR3				; clear all peripheral interrupt flags

go:
	bsf		PTCON1, PTEN		; time base on
	
loop8:							; infinite loop
	goto	loop8

	END